﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using mbhxnaengine.Code.Utilities;

namespace mbhxnaengine.Code.GameObject
{
    // Singleton class which manages all the GameObject instances of the engine.
    // By calling update and Render on this class, all the GameObjects it contains
    // will be updated and rendered.
    //
    class GameObjectManager
    {    
        // The static instance of this class, making this a singleton.
        //
        static private GameObjectManager mInstance = null;

        // A list of all the GameObjects in the game.  The main purpose
        // of this class is to manage this list.
        //
        private List<GameObject> mGameObjects;

        // A list of all the GameObjects that need to be added at the next possible
        // time.  We can't just add the objects right away because we could be 
        // interating through the list at the time.
        //
        private List<GameObject> mGameObjectsToAdd;

        // A list of all the GameObjects that need to be removed at the next possible
        // time.  We can't just remove the objects right away because we could be 
        // interating through the list at the time.
        //
        private List<GameObject> mGameObjectsToRemove;

        // Holds on to an instance of the ContentManager so that we can load new objects
        // on the fly.
        //
        // This probably shouldn't be here.  For one, we probably want to preload most of
        // our content.  In the cases where we don't we should probably have some sort of
        // ContentManager...Manager.  That feels a little weird right now, so I am just 
        // putting this here for now, and we can re-examine this concent once things are a
        // little more flushed out.
        //
        private ContentManager mContent;

        // We make the constructor private so that no one accidentally creates
        // an instance of the class.
        //
        private GameObjectManager()
        {
            mGameObjects            = new List<GameObject>();
            mGameObjectsToAdd       = new List<GameObject>();
            mGameObjectsToRemove    = new List<GameObject>();
        }

        // Needs to be called before any of the usage methods are called.
        //
        public void Initialize(ContentManager content)
        {
            mContent = content;
        }

        // Should be called once per frame.
        //
        public void Update(GameTime gameTime)
        {
            // Keep track of how many objects were updated this frame.
            int count = 0;

            // First update every object we are managing.
            //
            foreach (GameObject go in mGameObjects)
            {
                go.Update(gameTime);

                count++;
            }

            DebugMessageDisplay.pInstance.AddDynamicMessage("Updated " + count + " GameObjects");

            // Now loop through our list of objects which need to be added and
            // add them.
            //
            foreach (GameObject go in mGameObjectsToAdd)
            {
                mGameObjects.Add(go);
            }
            mGameObjectsToAdd.Clear();

            // Now loop through our list of objects which need to be removed and
            // remove them.
            //
            foreach (GameObject go in mGameObjectsToRemove)
            {
                mGameObjects.Remove(go);
            }
            mGameObjectsToRemove.Clear();
        }

        // Should be called once per frame.
        //
        public void Render(SpriteBatch batch)
        {
            foreach (GameObject go in mGameObjects)
            {
                go.Render(batch);
            }
        }

        // Interface for adding a game object to the manger.
        //
        public void Add(GameObject go)
        {
            mGameObjectsToAdd.Add(go);
        }

        // Interface for implicitly removing a game object from the manager.
        //
        public void Remove(GameObject go)
        {
            mGameObjectsToRemove.Add(go);
        }

        // Accessor to the pContent property.
        //
        public ContentManager pContentManager
        {
            get
            {
                return mContent;
            }
        }

        // Accessor to the pInstance property.
        //
        public static GameObjectManager pInstance
        {
            get
            {
                // If this is the first time this is called, instantiate our
                // static instance of the class.
                if (mInstance == null)
                {
                    mInstance = new GameObjectManager();
                }

                // Either way, at this point we should have an instantiated version
                // if the class.
                return mInstance;
            }
        }

    }
}
